# -*- coding: utf-8 -*-
"""
中性策略框架 | 邢不行 | 2024分享会
author: 邢不行
微信: xbx6660
"""
import os
import pandas as pd

# 取出策略文件名中_后的部分
stg_name = os.path.basename(__file__).split('.')[0].split('_')[1]

# 持仓周期。目前回测支持日线级别、小时级别。例：1H，6H，3D，7D......
# 当持仓周期为D时，选币指标也是按照每天一根K线进行计算。
# 当持仓周期为H时，选币指标也是按照每小时一根K线进行计算。
hold_period = '1D'.replace('h', 'H').replace('d', 'D')
# 配置offset
offset = 0
# 是否使用现货
if_use_spot = True  # True：使用现货。False：不使用现货，只使用合约。

# 多头选币数量。1 表示做多一个币; 0.1 表示做多10%的币
long_select_coin_num = 0.1
# 空头选币数量。1 表示做空一个币; 0.1 表示做空10%的币
# short_select_coin_num = 0.1
short_select_coin_num = 'long_nums'  # long_nums意为着空头数量和多头数量保持一致。最多为所有合约的数量。注意：多头为0的时候，不能配置'long_nums'

# 多头的选币因子列名。
long_factor = '因子'  # 因子：表示使用复合因子，默认是 factor_list 里面的因子组合。需要修改 calc_factor 函数配合使用
# 空头的选币因子列名。多头和空头可以使用不同的选币因子
short_factor = '因子'

############################################
# 由贝叶斯炼丹炉自动生成的代码
############################################

# @$开始定义选币炼丹结果数据$@#

# 炼丹炉说明：
# 1、在炼丹基础策略中，需要在下面定义所有参与炼丹的因子列表，这样在运行 `1_选币数据整理.py` 时才会计算因子数据
# 2、炼丹完成后，下面的代码会被替换为炼丹生成的代码

# 选币因子信息列表，用于`2_选币_单offset.py`，`3_计算多offset资金曲线.py`共用计算资金曲线
factor_list = [
    # 因子名（和factors文件中相同），排序方式，参数，权重。
    ('AbsChg', True, 7, 1),
    ('AdaptBollingv3', True, 7, 1),
    ('Adosc', True, 7, 1),
    ('Adtm', True, 7, 1),
    ('Adtmv2', True, 7, 1),
    ('Adtmv3', True, 7, 1),
    ('Adx+mtm', True, 7, 1),
    ('Adx-mtm', True, 7, 1),
    ('Adx', True, 7, 1),
    ('AdxDi+', True, 7, 1),
    ('AdxDi-', True, 7, 1),
    ('Adxr', True, 7, 1),
    ('Adxrneg', True, 7, 1),
    ('Adxrpos', True, 7, 1),
    ('Adxrv2', True, 7, 1),
    ('Amv', True, 7, 1),
    ('Apz', True, 7, 1),
    ('ApzLower', True, 7, 1),
    ('ApzUpper', True, 7, 1),
    ('Ar', True, 7, 1),
    ('ArbrAr', True, 7, 1),
    ('ArbrBr', True, 7, 1),
    ('Arron', True, 7, 1),
    ('Atr', True, 7, 1),
    ('Atrcount', True, 7, 1),
    ('AtrLower', True, 7, 1),
    ('AtrPct', True, 7, 1),
    ('AtrUpper', True, 7, 1),
    ('AvgPrice', True, 7, 1),
    ('AvgPriceToHigh', True, 7, 1),
    ('AvgPriceToLow', True, 7, 1),
    ('Bbi', True, 7, 1),
    ('BbiBias', True, 7, 1),
    ('Bbw', True, 7, 1),
    ('Bias', True, 7, 1),
    ('Bias36', True, 7, 1),
    ('Bias36ma', True, 7, 1),
    ('BiasCubicv2', True, 7, 1),
    ('Biasema', True, 7, 1),
    ('Biasv1', True, 7, 1),
    ('Biasv10', True, 7, 1),
    ('Biasv11', True, 7, 1),
    ('Biasv13', True, 7, 1),
    ('Biasv14', True, 7, 1),
    ('Biasv3', True, 7, 1),
    ('Biasv4', True, 7, 1),
    ('BiasVol', True, 7, 1),
    ('Bir', True, 7, 1),
    ('Bollcount', True, 7, 1),
    ('Bollcountdem', True, 7, 1),
    ('BollCountPunish', True, 7, 1),
    ('Bollcountv5', True, 7, 1),
    ('Bollcountv7', True, 7, 1),
    ('Bolling', True, 7, 1),
    ('Bollingfancy', True, 7, 1),
    ('Bollingv2', True, 7, 1),
    ('Bollingv3', True, 7, 1),
    ('Bollingv4', True, 7, 1),
    ('BollingWidth', True, 7, 1),
    ('Bop', True, 7, 1),
    ('Br', True, 7, 1),
    ('Burr', True, 7, 1),
    ('Cci', True, 7, 1),
    ('CciMagic', True, 7, 1),
    ('Cciv3', True, 7, 1),
    ('ChangeStd', True, 7, 1),
    ('Chlafancy', True, 7, 1),
    ('Clv', True, 7, 1),
    ('Cmf', True, 7, 1),
    ('Cmo', True, 7, 1),
    ('Cmov2', True, 7, 1),
    ('Cmov3', True, 7, 1),
    ('Copp', True, 7, 1),
    ('CoppMinRoute', True, 7, 1),
    ('Coppv3', True, 7, 1),
    ('Cr', True, 7, 1),
    ('Cse', True, 7, 1),
    ('Csmtmv2', True, 7, 1),
    ('Csmtmv3', True, 7, 1),
    ('Cv', True, 7, 1),
    ('Cvrv0', True, 7, 1),
    ('Damaov10', True, 7, 1),
    ('Dbcd', True, 7, 1),
    ('Dbcdv2', True, 7, 1),
    ('Dbcdv3', True, 7, 1),
    ('Dc', True, 7, 1),
    ('DcSignal', True, 7, 1),
    ('Dcv2', True, 7, 1),
    ('Dema', True, 7, 1),
    ('Demaker', True, 7, 1),
    ('DiffEma', True, 7, 1),
    ('Do', True, 7, 1),
    ('Dpo', True, 7, 1),
    ('DzcciLower', True, 7, 1),
    ('DzcciLowerSignal', True, 7, 1),
    ('DzcciLowerSignalv2', True, 7, 1),
    ('DzcciUpper', True, 7, 1),
    ('DzcciUpperSignal', True, 7, 1),
    ('DzcciUpperSignalv2', True, 7, 1),
    ('DzrsiLowerSignal', True, 7, 1),
    ('DzrsiUpperSignal', True, 7, 1),
    ('Emv', True, 7, 1),
    ('EnvLower', True, 7, 1),
    ('EnvLowerSignal', True, 7, 1),
    ('EnvSignal', True, 7, 1),
    ('EnvUpper', True, 7, 1),
    ('EnvUpperSignal', True, 7, 1),
    ('Er', True, 7, 1),
    ('Erbear', True, 7, 1),
    ('Erbull', True, 7, 1),
    ('Expma', True, 7, 1),
    ('FbLower', True, 7, 1),
    ('FbLowerSignal', True, 7, 1),
    ('FbLowerSignalv2', True, 7, 1),
    ('FbLowerSignalv3', True, 7, 1),
    ('FbLowerv2', True, 7, 1),
    ('FbLowerv3', True, 7, 1),
    ('Fbnqpctv5', True, 7, 1),
    ('FbUpper', True, 7, 1),
    ('FbUpperSignal', True, 7, 1),
    ('FbUpperSignalv2', True, 7, 1),
    ('FbUpperSignalv3', True, 7, 1),
    ('FbUpperv2', True, 7, 1),
    ('FbUpperv3', True, 7, 1),
    ('FearGreedYidaiv1', True, 7, 1),
    ('Fi', True, 7, 1),
    ('FiRsi', True, 7, 1),
    ('Fisher', True, 7, 1),
    ('Fisherv2', True, 7, 1),
    ('Fisherv3', True, 7, 1),
    ('Force', True, 7, 1),
    ('Grid', True, 7, 1),
    ('Hlma', True, 7, 1),
    ('Hma', True, 7, 1),
    ('HmaSignal', True, 7, 1),
    ('Hullma', True, 7, 1),
    ('HullmaSignal', True, 7, 1),
    ('Ic', True, 7, 1),
    ('Icv2', True, 7, 1),
    ('Icv3', True, 7, 1),
    ('Icv4', True, 7, 1),
    ('ILLQMean', True, 7, 1),
    ('ILLQStd', True, 7, 1),
    ('Imi', True, 7, 1),
    ('KcLower', True, 7, 1),
    ('KcLowerSignal', True, 7, 1),
    ('KcSignal', True, 7, 1),
    ('KcUpper', True, 7, 1),
    ('KcUpperSignal', True, 7, 1),
    ('KdjD', True, 7, 1),
    ('KdjdD', True, 7, 1),
    ('KdjdK', True, 7, 1),
    ('KdjJ', True, 7, 1),
    ('KdjK', True, 7, 1),
    ('Ke', True, 7, 1),
    ('Ko', True, 7, 1),
    ('Kst', True, 7, 1),
    ('Lchcfancy', True, 7, 1),
    ('Lcsd', True, 7, 1),
    ('Lma', True, 7, 1),
    ('LongMoment', True, 7, 1),
    ('Ma', True, 7, 1),
    ('Maamt', True, 7, 1),
    ('Mac', True, 7, 1),
    ('Macd', True, 7, 1),
    ('Macdv2', True, 7, 1),
    ('MacdVol', True, 7, 1),
    ('MaConverge', True, 7, 1),
    ('Macv2', True, 7, 1),
    ('Macv3', True, 7, 1),
    ('Macv4', True, 7, 1),
    ('Macv5', True, 7, 1),
    ('MadisPlaced', True, 7, 1),
    ('MagicCci', True, 7, 1),
    ('MagicCciv2', True, 7, 1),
    ('Mak', True, 7, 1),
    ('Makv2', True, 7, 1),
    ('MarketPl', True, 7, 1),
    ('MarketPlv2', True, 7, 1),
    ('MaSignal', True, 7, 1),
    ('Mfi', True, 7, 1),
    ('Micd', True, 7, 1),
    ('Mm', True, 7, 1),
    ('Mssi', True, 7, 1),
    ('Mtm', True, 7, 1),
    ('MtmHcm', True, 7, 1),
    ('MtmMax', True, 7, 1),
    ('MtmMean', True, 7, 1),
    ('MtmMeanGap', True, 7, 1),
    ('MtmMeanv10', True, 7, 1),
    ('MtmMeanv11', True, 7, 1),
    ('MtmMeanv2', True, 7, 1),
    ('MtmMeanv8', True, 7, 1),
    ('MtmVolMean', True, 7, 1),
    ('MtmVolResonance', True, 7, 1),
    ('NetVolfancy', True, 7, 1),
    ('Nvi', True, 7, 1),
    ('Obv', True, 7, 1),
    ('Obvv2', True, 7, 1),
    ('Osc', True, 7, 1),
    ('Pac', True, 7, 1),
    ('PacLower', True, 7, 1),
    ('PacLowerv2', True, 7, 1),
    ('PacUpper', True, 7, 1),
    ('PacUpperv2', True, 7, 1),
    ('PctChange', True, 7, 1),
    ('Pfe', True, 7, 1),
    ('PjcDistance', True, 7, 1),
    ('PmarpYidaiv1', True, 7, 1),
    ('Pmo', True, 7, 1),
    ('PmoTema', True, 7, 1),
    ('Po', True, 7, 1),
    ('Pos', True, 7, 1),
    ('Ppo', True, 7, 1),
    ('Ppov1', True, 7, 1),
    ('PriceVolumeResist', True, 7, 1),
    ('Psy', True, 7, 1),
    ('Pvi', True, 7, 1),
    ('Pvo', True, 7, 1),
    ('Pvt', True, 7, 1),
    ('Pvtv2', True, 7, 1),
    ('Pvtv3', True, 7, 1),
    ('Pvtv4', True, 7, 1),
    ('Qstick', True, 7, 1),
    ('QuanlityPriceCorr', True, 7, 1),
    ('QuoteVolumeMean', True, 7, 1),
    ('QuoteVolumeRatio', True, 7, 1),
    ('Rbias', True, 7, 1),
    ('Rccd', True, 7, 1),
    ('Rccdv2', True, 7, 1),
    ('Regv3', True, 7, 1),
    ('RetBollfancy', True, 7, 1),
    ('Rmi', True, 7, 1),
    ('Roc', True, 7, 1),
    ('RocVol', True, 7, 1),
    ('Rsi', True, 7, 1),
    ('RsiBbw', True, 7, 1),
    ('Rsih', True, 7, 1),
    ('Rsimean', True, 7, 1),
    ('Rsis', True, 7, 1),
    ('Rsisv2', True, 7, 1),
    ('Rsiv', True, 7, 1),
    ('Rsiv2', True, 7, 1),
    ('Rsiv3', True, 7, 1),
    ('Rsj', True, 7, 1),
    ('Rvi', True, 7, 1),
    ('Rwi', True, 7, 1),
    ('RwiH', True, 7, 1),
    ('RwiL', True, 7, 1),
    ('Sgcz', True, 7, 1),
    ('ShortMoment', True, 7, 1),
    ('Si', True, 7, 1),
    ('Skdj', True, 7, 1),
    ('Smi', True, 7, 1),
    ('Smiv2', True, 7, 1),
    ('Sroc', True, 7, 1),
    ('SrocVol', True, 7, 1),
    ('Stc', True, 7, 1),
    ('T3', True, 7, 1),
    ('Tdi', True, 7, 1),
    ('Tema', True, 7, 1),
    ('Tii', True, 7, 1),
    ('TiiSignal', True, 7, 1),
    ('TiiSignalv2', True, 7, 1),
    ('Tma', True, 7, 1),
    ('TmaBias', True, 7, 1),
    ('Tmav2', True, 7, 1),
    ('Tmav3', True, 7, 1),
    ('Tmf', True, 7, 1),
    ('TradeNum', True, 7, 1),
    ('TradeNumMeanV1', True, 7, 1),
    ('TrendZhangDieFu', True, 7, 1),
    ('Trix', True, 7, 1),
    ('TrTrix', True, 7, 1),
    ('Trv', True, 7, 1),
    ('Tsi', True, 7, 1),
    ('Turtle', True, 7, 1),
    ('Typ', True, 7, 1),
    ('Uos', True, 7, 1),
    ('UpNumfancy', True, 7, 1),
    ('V1', True, 7, 1),
    ('V1Dn', True, 7, 1),
    ('V1Dnv2', True, 7, 1),
    ('V1Up', True, 7, 1),
    ('V1Upv2', True, 7, 1),
    ('V1v2', True, 7, 1),
    ('Vao', True, 7, 1),
    ('Vaov2', True, 7, 1),
    ('Vi+', True, 7, 1),
    ('Vi-', True, 7, 1),
    ('Vi', True, 7, 1),
    ('Vidya', True, 7, 1),
    ('Vidyav2', True, 7, 1),
    ('Vidyav3', True, 7, 1),
    ('Vidyav4', True, 7, 1),
    ('Vidyav5', True, 7, 1),
    ('VixBw', True, 7, 1),
    ('Vma', True, 7, 1),
    ('VolPerTradefancy', True, 7, 1),
    ('Volume', True, 7, 1),
    ('VolumeStd', True, 7, 1),
    ('Vr', True, 7, 1),
    ('VRA', True, 7, 1),
    ('Vramt', True, 7, 1),
    ('Vwap', True, 7, 1),
    ('VwapBbw', True, 7, 1),
    ('Vwapbias', True, 7, 1),
    ('VwapSignal', True, 7, 1),
    ('Wad', True, 7, 1),
    ('Wc', True, 7, 1),
    ('Wr', True, 7, 1),
    ('Wvad', True, 7, 1),
    ('ZhangDieFu', True, 7, 1),
    ('ZhangDieFuAllHour', True, 7, 1),
    ('ZhangDieFuSkew', True, 7, 1),
    ('ZhangDieFuStd', True, 7, 1),
    ('ZhenFu', True, 7, 1),
    ('ZhenFuBear', True, 7, 1),
    ('ZhenFuBull', True, 7, 1),
    ('ZhenFuStd', True, 7, 1),
    ('ZhenFuv2', True, 7, 1),
    ('ZhenZhangRatio', True, 7, 1),
    ('Zlmacd', True, 7, 1),
    ('涨跌幅max', True, 7, 1),
    ('风格动量', True, 7, 1),
    ('风格波动', True, 7, 1),
    ('风格流动性', True, 7, 1),
]

# 确认过滤因子及其参数，用于`2_选币_单offset.py`进行过滤
filter_list = [
    # 因子名（和factors文件中相同），参数
    ('PctChange', 7),
    ('QuoteVolumeMean', 7),
    ('ZhangDieFu',7),
    ('涨跌幅max',7),
    ('Bias', 7),
    ('MtmVolMean', 7),
    ('TradeNumMeanV1', 7),
    ('FbUpperSignalv3', 7),
    ('MacdVol', 7),
]

bys_result_user_params = {}

# @$结束定义选币炼丹结果数据$@#

############################################
# 用于贝叶斯炼丹的参数
############################################

bys_user_param_dict = {
    "filter_rank": {
        "下限": 0.50,
        "上限": 0.95,
        "步长": 0.05,
    },
}

# 炼丹使用过滤因子数量的最小和最大值，如果为 None 则使用 bys_config.py 中的配置
bys_filter_factor_min_count = 1
bys_filter_factor_max_count = 1

# 排序因子参数的默认值，如果为 None 则使用 bys_config.py 中的配置
bys_sort_factor_default = None

# 炼丹使用排序因子数量的最小和最大值，如果为 None 则使用 bys_config.py 中的配置
bys_sort_factor_min_count = 1
bys_sort_factor_max_count = 2

# 回测结果评分参数，如果为 None 则使用 bys_config.py 中的配置
bys_eval_score_dict = None

def after_merge_index(df, symbol, factor_dict, data_dict):
    """
    合并指数数据之后的处理流程，非必要。
    本函数住要的作用如下：
            1、指定K线上不常用的数据在resample时的规则，例如：'taker_buy_quote_asset_volume': 'sum'
            2、合并外部数据，并指定外部数据在resample时的规则。例如：（伪代码）
                    chain_df = pd.read_csv(chain_path + symbol)  # 读取指定币种链上数据
                    df = pd.merge(df,chain_df,'left','candle_begin_time') # 将链上数据合并到日线上
                    factor_dict['gas_total'] = 'sum' # 链上的gas_total字段，在小时数据转日线数据时用sum处理
                    data_dict['gas_total'] = 'sum' # 链上的gas_total字段，在最后一次resample中的处理规则
    :param df:
    :param symbol:
    :param factor_dict: 小时级别resample到日线级别时使用(计算日线级别因子时需要，计算小时级别因子时不需要)
    :param data_dict: resample生成最终数据时使用
    :return:
    """

    # 专门处理转日线是的resample规则
    factor_dict['taker_buy_quote_asset_volume'] = 'sum'  # 计算日线级别因子前先resample到日线数据
    factor_dict['trade_num'] = 'sum'

    return df, factor_dict, data_dict


def after_resample(df, symbol):
    """
    数据重采样之后的处理流程，非必要
    :param df:          传入的数据
    :param symbol:      币种名称
    :return:
    """

    return df


# =====================以上是数据整理部分封装转的策略代码==========================
# ============================================================================
# ============================================================================
# ============================================================================
# ============================================================================
# =======================以下是选币函数封装的策略代码=============================

# 炼丹时只加载必须用到的因子，此处需要列出在 calc_factor 中使用到的因子，
# 以避免这些因子没有被保留。
user_keep_cols = []

def user_cal_score(
    score,
    tp,
    rtn: pd.DataFrame,
    year_return: pd.DataFrame,
    month_return: pd.DataFrame,
    ret_dict: dict,
    core_dict: dict,
    is_sample: bool,
):
    """
    用户自定义回测评分函数，可以根据自己的需求进行修改
    :param score: 炉子根据配置计算的评分结果
    :param tp: 回测结果类型：中性、多头、空头
    :param rtn: 回测结果
    :param year_return: 年回报数据
    :param month_return: 月回报数据
    :param ret_dict: 保存到 学习结果.csv 文件中的数据
    :param core_dict: 保存到 高分数据.txt 文件中的数据
    :param is_sample: 如果为 True 表示计算炼丹时的样本日期内评分，如果为 False 表示是样本外日期回测
    :return: 计算完成的评分结果
    """

    """
    # 以下代码演示了在总分中增加基于月回报波动率的得分
    # 1.计算月波动率
    month_std_mean = month_return['涨跌幅'].map(lambda x: float(x.replace('%', ''))).std() / \
        month_return['涨跌幅'].map(lambda x: float(x.replace('%', ''))).mean()

    # 2.计算得分
    h_val = 1
    l_val = 3
    h_score = 30
    month_score = (month_std_mean - l_val) / (h_val - l_val) * h_score

    # 3.返回结果
    ret_dict['月回报波动'] = month_std_mean
    core_dict[f'月回报波动_得分'] = round(month_score, 2)
    score += month_score
    """
    return score, ret_dict, core_dict

def calc_factor(df, **kwargs):
    """
    计算因子

    多空使用相同的两个因子

        1。多空相同的单因子选币，不需要编写代码

        2。多空相同的复合因子，本案例代码不需要修改，直接使用即可。《本案例代码就是多空相同的复合因子选币》

    多空使用不同的两个因子

        1。多空不同的单因子选币，不需要编写代码

        2。多空分离选币，需要根据具体因子具体改写
            !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            !!!!!这里改写需要一定的代码能力!!!!!
            !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            多空存在某一方是复合因子，另一方是单因子，则只需要计算一个复合因子即可。代码可以参考下方案例代码

            如果多空都是复合因子，多空需要分别是计算复合因子。

    :param df:          原数据
    :return:
    """
    # 接受外部回测的因子列表，这里主要是去适配`4_遍历选币参数.py`
    external_list = kwargs.get('external_list', [])
    if external_list:  # 如果存在外部回测因子列表，则使用外部因子列表
        _factor_list = external_list
    else:  # 如过不存在外部回测因子列表，默认使用当前策略的因子列表
        _factor_list = factor_list

    # 多空相同的复合因子计算
    if long_factor == short_factor == '因子':
        df[long_factor] = 0
        for factor_name, if_reverse, parameter_list, weight in _factor_list:
            col_name = f'{factor_name}_{str(parameter_list)}'
            # 计算单个因子的排名
            df[col_name + '_rank'] = df.groupby('candle_begin_time')[col_name].rank(ascending=if_reverse, method='min')
            # 将因子按照权重累加
            df[long_factor] += (df[col_name + '_rank'] * weight)

    return df


def before_filter(df, **kwargs):
    """
    前置过滤函数
    自定义过滤规则，可以对多空分别自定义过滤规则

    :param df:                  原始数据
    :return:                    过滤后的数据
    """
    # 接受外部回测的因子列表，这里主要是去适配`5_查看历年参数平原.py`
    ex_filter_list = kwargs.get('ex_filter_list', [])
    if ex_filter_list:  # 如果存在外部回测因子列表，则使用外部因子列表
        _filter_list = ex_filter_list
    else:  # 如过不存在外部回测因子列表，默认使用当前策略的因子列表
        _filter_list = filter_list

    df_long = df.copy()
    df_short = df.copy()

    # 如果过滤列表中只有一个因子
    if len(_filter_list) == 1:
        filter_factor = _filter_list[0][0] + '_' + str(_filter_list[0][1])
        # 配置了过滤因子信息，则进行过滤操作
        filter_rank = bys_result_user_params.get('filter_rank', 0.8)
        df_long['filter_rank'] = df_long.groupby('candle_begin_time')[filter_factor].rank(ascending=True, pct=True)
        df_long = df_long[(df_long['filter_rank'] < filter_rank)]

        df_short['filter_rank'] = df_short.groupby('candle_begin_time')[filter_factor].rank(ascending=True, pct=True)
        df_short = df_short[(df_short['filter_rank'] < filter_rank)]
    else:  # 如果使用多个因子进行过滤，在这里进行填写

        pass

    return df_long, df_short
